/* 
 * $Id: Builder.java 1259 2007-01-09 14:23:47Z tcoupaye $ 
 * 
 * Behavior Protocols extensions for static and runtime checking
 * developed for the Julia implementation of Fractal.
 *
 * Copyright (C) 2004
 *    Distributed Systems Research Group
 *    Department of Software Engineering
 *    Faculty of Mathematics and Physics
 *    Charles University, Prague
 *
 * Copyright (C) 2006
 *    Formal Methods In Software Engineering Group
 *    Institute of Computer Science
 *    Academy of Sciences of the Czech Republic
 *
 * Copyright (C) 2006 France Telecom
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 * Contact: ft@nenya.ms.mff.cuni.cz
 * Authors: Stanislav Visnovsky <visnovsky@nenya.ms.mff.cuni.cz>,
 *          Jan Kofron <kofron@nenya.ms.mff.cuni.cz>
 */
 

package org.objectweb.fractal.bpc.checker.parser;

import java.io.*;
import java.util.*;

import org.objectweb.fractal.bpc.checker.DFSR.Options;
import org.objectweb.fractal.bpc.checker.node.*;


/**
 * This original code was written by
 * 
 * Stanislav Visnovsky
 * 
 * to be used by his checker in SOFAnode. I decided to reuse this part because
 * the parser (class Builder) is exactly what I needed for my
 * org.objectweb.fractal.bpc.checker.
 * 
 * The class for building the automata from a given behavior protocol. A
 * protocol is passed not into constructor, thus, one instance can be used for
 * building any number of automata.
 */
public class Builder {

    /**
     * multinode optimization enabled
     */
    private boolean MULTINODES = true;

    /**
     * next char for LL(1) parsing
     */
    private char next_char;

    /**
     * a reader which contains a behavior protocol to be parsed and built
     */
    private ProtocolReader source;

    /**
     * next token for LL(1) parsing
     */
    private String next_token;

    /**
     * flag for indicating the action token read
     */
    private boolean isTokenID;

    /**
     * action repository for storing the action names and their indices
     */
    private ActionRepository repository;

    /**
     * protocol being parsed
     */
    private String protocol;
    
    /**
     * runtime checking tag
     */
    private boolean runtime = false;

    /**
     * Creates new instance of Builder and stores the reference to the action
     * repository object
     */
    public Builder(ActionRepository repository) {
        this.repository = repository;
        this.MULTINODES = Options.multinodes;
    }

    /**
     * Inverse of the action Check the first character of the string and returns
     * the complementary action
     * 
     * @param src source string containing the action description
     * @return the string containing the complement operation
     */
    static private String getReverseAction(String src) {
        String e;
        if (src.startsWith("!"))
            e = (new String("?")).concat(src.substring(1));
        else if (src.startsWith("?"))
            e = (new String("!")).concat(src.substring(1));
        else
            e = new String(src);
        return e;
    }

    /**
     * Check if the given action is a simple call (i.e. not a response, a
     * request, a general event and a special event)
     * 
     * @param src source string containing the action description
     * @return true if this is a simple call
     */
    static private boolean isSimpleCall(String src) {
        return !(src.endsWith("^") || src.endsWith("$") || src.endsWith("~") || src.endsWith("["));
    }

    /**
     * Test for character to be a letter (case insensitive).
     * @param ch character to be tested
     * @return result of the test
     * @see Builder#nextToken
     */
    private boolean isLetter(char ch) {
        return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_') || (ch >= '0' && ch <= '9') || (ch == ':');
    }

    /**
     * Test for character to be a whitespace.
     * 
     * @param ch
     *            character to be tested
     * @return result of the test
     */
    public static boolean isWhiteSpace(char ch) {
        return ((ch == ' ') || (ch == '\t') || (ch == '\n') || (ch == '\r'));
    }
    
    private void nextPart(String part) {
        protocol += part;
        Debug.print(2, part);
    }

    /**
     * Read the next token from the reader. In case of action token, indicate it
     * by the isTokenID flag.
     * 
     * @return read token, or token EOF
     * @throws SyntaxErrorException
     *             in case of badly formed protocols
     * @see Builder#isTokenID
     */
    private String nextToken() throws SyntaxErrorException, IOException {
        String result;
        isTokenID = false;
        if (!source.ready()) {
            Debug.println(2, "");
            return "";
        }
        while (isWhiteSpace(next_char))
            // skip whitespaces
            if (source.ready()) {
                protocol += " ";
                next_char = (char) source.read();
            }
            else {
                Debug.println(2, "");
                return "";
            }
        switch (next_char) {
        case (char) -1: {
            Debug.println(2, "");
            return "";
        }
        case '|':
            next_char = (char) source.read();
            if (next_char == '|') {
                next_char = (char) source.read();
                nextPart("||");
                return "||";
            } else {
                nextPart("|");
                return "|";
            }
        case '&':
            next_char = (char) source.read();
            nextPart("&");
            return "&";
        case '@':
            next_char = (char) source.read();
            nextPart("@");
            return "@";
        case '.':
            next_char = (char) source.read();
            nextPart(".");
            return ".";
        case '*':
            next_char = (char) source.read();
            nextPart("*");
            return "*";
        case '+':
            next_char = (char) source.read();
            nextPart("+");
            return "+";
        case ';':
            next_char = (char) source.read();
            nextPart(";");
            return ";";
        case ',':
            next_char = (char) source.read();
            nextPart(",");
            return ",";
        case '\\':
            next_char = (char) source.read();
            nextPart("\\");
            return "\\";
        case '{':
            next_char = (char) source.read();
            nextPart("{");
            return "{";
        case '}':
            next_char = (char) source.read();
            nextPart("}");
            return "}";
        case '(':
            next_char = (char) source.read();
            nextPart("(");
            return "(";
        case ')':
            next_char = (char) source.read();
            nextPart(")");
            return ")";
        case '[':
            next_char = (char) source.read();
            nextPart("[");
            return "[";
            
        case ']':
            next_char = (char) source.read();
            nextPart("]");
            return "]";
            
        /*
        case '[':
            result = "[";
            next_char = (char) source.read();
            while (next_char != ']') {
                result += next_char;
                next_char = (char) source.read();
            }
            result += next_char;
            next_char = (char) source.read();
            isTokenID = true;
            nextPart(result);
            return result;
        */
        default:
            if (isLetter(next_char) || next_char == '?' || next_char == '!' || next_char == '<' || next_char == '#') {
                result = "";
                if (next_char == '!' || next_char == '?' || next_char == '#') {
                    result = result + next_char;
                    next_char = (char) source.read();
                }
                if (next_char == '<') {
                    do {
                        result = result + next_char;
                        next_char = (char) source.read();
                    } while (next_char != '>');
                    result = result + next_char;
                    next_char = (char) source.read();
                } else {
                    while (isLetter(next_char)) {
                        result = result + next_char;
                        next_char = (char) source.read();
                    }
                }
                if (next_char != '.') {
                    if (result.equals("NULL")) {
                        isTokenID = true;
                        nextPart(result);
                        return result;
                    }
                    throw new SyntaxErrorException("Invalid event name: dot delimiter expected in event name, found " + next_char + " after " + result);
                } else
                    result = result + next_char;
                next_char = (char) source.read();
                while (isLetter(next_char)) {
                    result = result + next_char;
                    next_char = (char) source.read();
                }
                if (next_char == '^' || next_char == '$' || next_char == '~') {
                    result = result + next_char;
                    next_char = (char) source.read();
                }
                isTokenID = true;
                nextPart(result);
                return result;
            }
            throw new SyntaxErrorException("Unexpected literal with code " + (int) next_char);
        }
    }


    /**
     * Builds protocol for + operator.
     * 
     * @return built protocol
     * @throws SyntaxErrorException
     *             in case of badly formed protocols
     * @throws IOException
     *             in case of unexpected end of reader
     *  
     */
    private TreeNode alternative() throws SyntaxErrorException, IOException {
        Vector nodelist = new Vector();

        Vector protindices = new Vector();
        protindices.add(new Integer(source.getIndex() - next_token.length() - 1));

        nodelist.add(sequence());
        while (next_token == "+") {
            next_token = nextToken();
            nodelist.add(sequence());
            protindices.add(new Integer(source.getIndex() - next_token.length() - (next_token.length() > 0 ? 1 : 0)));
        }

        //protindices.add(new
        // Integer(((Integer)protindices.lastElement()).intValue() - 1));

        if (nodelist.size() > 1) {
            if (MULTINODES) {
                TreeNode[] nodes = new TreeNode[nodelist.size()];
                for (int i = 0; i < nodelist.size(); ++i)
                    nodes[i] = (TreeNode) nodelist.get(i);

                TreeNode newnode = new AlternativeNode(nodes);
                newnode.protocol = protocol.substring(((Integer) protindices.get(0)).intValue(), ((Integer) protindices.lastElement()).intValue());
                return newnode;
            }

            else {
                TreeNode[] nodes = new TreeNode[2];
                TreeNode result = (TreeNode) nodelist.get(0);

                for (int i = 1; i < nodelist.size(); ++i) {
                    nodes = new TreeNode[2];
                    nodes[0] = result;
                    nodes[1] = (TreeNode) nodelist.get(i);
                    result = new AlternativeNode(nodes);
                    result.protocol = protocol.substring(((Integer) protindices.get(0)).intValue(), ((Integer) protindices.get(i)).intValue());
                }

                return result;
            }
        } else {
            return (TreeNode) nodelist.get(0);
        }
    }

    /**
     * Builds protocol for ; operator.
     * 
     * @return built automaton
     * @throws SyntaxErrorException
     *             in case of badly formed protocols
     * @throws IOException
     *             in case of unexpected end of reader
     * 
     *  
     */
    private TreeNode sequence() throws SyntaxErrorException, IOException {
        Vector nodelist = new Vector();
        Vector protindices = new Vector();
        protindices.add(new Integer(source.getIndex() - next_token.length() - 1));

        nodelist.add(andparallel());

        while (next_token == ";") {
            next_token = nextToken();
            nodelist.add(andparallel());
            protindices.add(new Integer(source.getIndex() - next_token.length() - (next_token.length() > 0 ? 1 : 0)));
        }

        //protindices.add(new
        // Integer(((Integer)protindices.lastElement()).intValue() - 1));

        if (nodelist.size() > 1) {
            if (MULTINODES) {
                TreeNode[] nodes = new TreeNode[nodelist.size()];
                for (int i = 0; i < nodelist.size(); ++i)
                    nodes[i] = (TreeNode) nodelist.get(i);

                TreeNode newnode = new SequenceNode(nodes);
                newnode.protocol = protocol.substring(((Integer) protindices.get(0)).intValue(), ((Integer) protindices.lastElement()).intValue());
                return newnode;
            } else {
                TreeNode[] nodes = new TreeNode[2];
                TreeNode result = (TreeNode) nodelist.get(0);

                for (int i = 1; i < nodelist.size(); ++i) {
                    nodes = new TreeNode[2];
                    nodes[0] = result;
                    nodes[1] = (TreeNode) nodelist.get(i);
                    result = new SequenceNode(nodes);
                    result.protocol = protocol.substring(((Integer) protindices.get(0)).intValue(), ((Integer) protindices.get(i)).intValue());
                }

                return result;
            }

        } else {
            return (TreeNode) nodelist.get(0);
        }
    }

    /**
     * Builds protocol for | operator.
     * 
     * @return built protocol
     * @throws SyntaxErrorException
     *             in case of badly formed protocols
     * @throws IOException
     *             in case of unexpected end of reader
     * 
     * @see Builder#orparallel
     */
    private TreeNode andparallel() throws SyntaxErrorException, IOException {
        Vector nodelist = new Vector();
        Vector protindices = new Vector();
        protindices.add(new Integer(source.getIndex() - next_token.length() - 1));

        nodelist.add(orparallel());
        while (next_token == "|") {
            next_token = nextToken();
            nodelist.add(orparallel());
            protindices.add(new Integer(source.getIndex() - next_token.length() - (next_token.length() > 0 ? 1 : 0)));
        }
        //protindices.add(new
        // Integer(((Integer)protindices.lastElement()).intValue() - 1));

        if (nodelist.size() > 1) {
            if (MULTINODES) {
                TreeNode[] nodes = new TreeNode[nodelist.size()];
                for (int i = 0; i < nodelist.size(); ++i)
                    nodes[i] = (TreeNode) nodelist.get(i);

                TreeNode newnode = new AndParallelNode(nodes);
                newnode.protocol = protocol.substring(((Integer) protindices.get(0)).intValue(), ((Integer) protindices.lastElement()).intValue());
                return newnode;
            } else {
                TreeNode[] nodes = new TreeNode[2];
                TreeNode result = (TreeNode) nodelist.get(0);

                for (int i = 1; i < nodelist.size(); ++i) {
                    nodes = new TreeNode[2];
                    nodes[0] = result;
                    nodes[1] = (TreeNode) nodelist.get(i);
                    result = new AndParallelNode(nodes);
                    result.protocol = protocol.substring(((Integer) protindices.get(0)).intValue(), ((Integer) protindices.get(i)).intValue());
                }

                return result;
            }

        } else {
            return (TreeNode) nodelist.get(0);
        }

    }

    /**
     * Builds protocol for || operator.
     * 
     * @return built protocol
     * @throws SyntaxErrorException
     *             in case of badly formed protocols
     * @throws IOException
     *             in case of unexpected end of reader
     * 
     * @see Builder#term
     */
    private TreeNode orparallel() throws SyntaxErrorException, IOException {
        Vector nodelist = new Vector();
        Vector protindices = new Vector();
        protindices.add(new Integer(source.getIndex() - next_token.length() - 1));

        nodelist.add(term());
        while (next_token == "||") {
            next_token = nextToken();
            nodelist.add(term());
            protindices.add(new Integer(source.getIndex() - next_token.length() - (next_token.length() > 0 ? 1 : 0)));
        }

        //protindices.set(protindices.size() - 1, new
        // Integer(((Integer)protindices.lastElement()).intValue() - 1));
        //protindices.add(new
        // Integer(((Integer)protindices.lastElement()).intValue() -
        // next_token.length()));

        TreeNode result = (TreeNode) nodelist.get(0);
        for (int i = 1; i < nodelist.size(); ++i) {
            
            TreeNode[] altnodes = new TreeNode[3];
            TreeNode[] apnodes = new TreeNode[2];
            apnodes[0] = result;
            apnodes[1] = (TreeNode) nodelist.get(i);
            altnodes[0] = result;
            altnodes[1] = (TreeNode) nodelist.get(i);
            altnodes[2] = new AndParallelNode(apnodes);
            result = new AlternativeNode(altnodes);
            ((AlternativeNode)result).setorparallel();
            //result = new OrParallelNode(result, (TreeNode) nodelist.get(i));
            result.protocol = protocol.substring(((Integer) protindices.get(0)).intValue(), ((Integer) protindices.get(i)).intValue());
        }

        return result;
    }

    /**
     * Parse a list of comma-separaded ids. Requires that all identifiers to be
     * already in EdgeFactory.ActionTokens.
     * 
     * @return read list of ids
     * @param endmark
     *            a string to mark the end of the list
     * @throws SyntaxErrorException
     *             in case of badly formed protocols
     * @throws IOException
     *             in case of unexpected end of reader
     * 
     * @see Builder#nextToken
     * @see Builder#composition
     * @see Builder#adjustment
     */
    private TreeSet collectList(String endmark) throws SyntaxErrorException, IOException {
        TreeSet result = new TreeSet();
        while (isTokenID) {
            if (next_token.startsWith("?") || next_token.startsWith("!") || next_token.startsWith("#")) {
                // this is action /action token
                if (next_token.endsWith("^") || next_token.endsWith("$") || next_token.endsWith("~")) {
                    // this is action token
                    Debug.println(3, "CollectList: action token");

                    result.add(new Integer(repository.addItem(next_token)));

                } else {
                    Debug.println(3, "CollectList: action token without");
                    // this is just action

                    result.add(new Integer(repository.addItem(next_token + "^")));

                    //append inverse action
                    result.add(new Integer(repository.addItem(getReverseAction(next_token + "$"))));

                }
            } else {
                // this is event / or event name
                if (next_token.endsWith("^") || next_token.endsWith("$") || next_token.endsWith("~")) {
                    Debug.println(3, "CollectList: adding event");
                    // this is event
                    result.add(new Integer(repository.addItem("?" + next_token)));

                    result.add(new Integer(repository.addItem("!" + next_token)));

                    if (endmark == "&") { // this is list for composition
                        result.add(new Integer(repository.addItem("#" + next_token)));
                    }
                } else {
                    Debug.println(3, "CollectList: adding event name");
                    // this is event name
                    result.add(new Integer(repository.addItem("?" + next_token + "^")));

                    result.add(new Integer(repository.addItem("?" + next_token + "$")));

                    result.add(new Integer(repository.addItem("!" + next_token + "^")));

                    result.add(new Integer(repository.addItem("!" + next_token + "$")));

                    if (endmark == "&") { // this is list for composition
                        result.add(new Integer(repository.addItem("#" + next_token + "^")));

                        result.add(new Integer(repository.addItem("#" + next_token + "$")));

                    }
                }
            }
            next_token = nextToken();
            if (next_token == endmark) {
                Debug.println(2, "End mark reached ");
                next_token = nextToken();
                return result;
            }
            if (next_token.compareTo(",") != 0)
                throw new SyntaxErrorException("Comma expected in list");
            else
                next_token = nextToken();
        }
        if (next_token == endmark) {
            Debug.println(2, "End mark reached");
            next_token = nextToken();
            return result;
        } else
            throw new SyntaxErrorException("List syntax error");
    }

    /**
     * Builds automaton for restriction operator by invoking the Restrict()
     * operation.
     * 
     * @return built automaton
     * @throws SyntaxErrorException
     *             in case of badly formed protocols
     * @throws IOException
     *             in case of unexpected end of reader
     * 
     * @see Builder#collectList
     */
    private TreeNode restriction() throws SyntaxErrorException, IOException {
        int protstart = source.getIndex() - next_token.length() - 1;
        TreeNode result = alternative();

        while (next_token == "\\") {
            Debug.println(2, "Restriction found");
            next_token = nextToken();
            if (next_token != "(")
                throw new SyntaxErrorException("Restriction list expected");
            next_token = nextToken();
            TreeSet sync = collectList(")");
            Debug.println(2, "Restriction action tokens: " + sync.toString());

            /*
             * result.Restrict( new ActionTokenArray(sync ) );
             * 
             * result = new ProtocolRestriction( result, sync );
             */
            result = new RestrictionNode(result, sync, repository);
            result.protocol = protocol.substring(protstart, source.getIndex() - next_token.length());
        }

        return result;
    }

    /**
     * Builds automaton for composition operator by invoking the
     * createComposition.
     * 
     * @return built automaton
     * @throws SyntaxErrorException
     *             in case of badly formed protocols
     * @throws IOException
     *             in case of unexpected end of reader
     * 
     * @see Builder#alternative
     */
    private TreeNode composition() throws SyntaxErrorException, IOException {
        int protstart = source.getIndex() - next_token.length() - 1;

        TreeNode node1 = restriction();

        while (next_token == "&") {
            Debug.println(2, "Composition found");
            next_token = nextToken();
            TreeSet sync = collectList("&");
            Debug.println(2, "Composition action tokens: " + sync.toString());

            /*
             * Protocol m = restriction(); result = new ProtocolComposition(
             * result, m, sync );
             */

            TreeNode node2 = restriction();
            node1 = new CompositionNode(node1, node2, sync, repository);
            node1.protocol = protocol.substring(protstart, source.getIndex() - next_token.length());

        }
        return node1;
    }

    /**
     * Builds protocol for adjustment operator.
     * 
     * @return built protocol
     * @throws SyntaxErrorException
     *             in case of badly formed protocols
     * @throws IOException
     *             in case of unexpected end of reader
     * 
     * @see Builder#composition
     */
    private TreeNode adjustment() throws SyntaxErrorException, IOException {
        int protstart = source.getIndex() - next_token.length() - 1;

        TreeNode node1 = composition();

        while (next_token == "@") {
            Debug.println(2, "Adjustment found");
            next_token = nextToken();
            TreeSet sync = collectList("@");
            Debug.println(2, "Adjustment action tokens: " + sync.toString());

            /*
             * Protocol m = composition(); result = new ProtocolAdjustment(
             * result, m, new ActionTokenArray(sync) );
             */
            TreeNode node2 = composition();
            node1 = new AdjustmentNode(node1, node2, sync);
            node1.protocol = protocol.substring(protstart, source.getIndex() - next_token.length());

        }
        return node1;
    }

    /**
     * Builds the automata for unary operator *.
     * 
     * @return built automaton
     * @throws SyntaxErrorException
     *             in case of badly formed protocols
     * @throws IOException
     *             in case of unexpected end of reader
     * 
     * @see Builder#factor
     *  
     */
    private TreeNode term() throws SyntaxErrorException, IOException {
        int protstart = source.getIndex() - next_token.length() - 1;

        TreeNode result = factor();

        if (next_token == "*") {
            next_token = nextToken();
            result = new RepetitionNode(result); //(Options.action == Options.ACTIONTESTNOCONSENT ? ((TreeNode)new ComplianceRepetitionNode(result)) : ((TreeNode)new RepetitionNode(result)));
            result.protocol = protocol.substring(protstart, source.getIndex() - next_token.length());

        }

        return result;
    }

    /**
     * Builds the basic automata for action tokens and NULL. Also, parentheses
     * are handled here.
     * 
     * @return built automaton
     * @throws SyntaxErrorException
     *             in case of badly formed protocols
     * @throws IOException
     *             in case of unexpected end of reader
     * 
     * @see Builder#alternative
     */
    private TreeNode factor() throws SyntaxErrorException, IOException {
        TreeNode result;
        if (next_token.equals("NULL")) {
            result = new NullNode();
            result.protocol = "null";
            next_token = nextToken();
        }

        else if (next_token == "(") {
            next_token = nextToken();
            result = adjustment();
            if (next_token != ")") {
                throw new SyntaxErrorException(") expected");
            }
            next_token = nextToken();
        } 
        
        else if (next_token == "[") {
            Debug.println(2, "Atomic action found");
            next_token = nextToken();
            
            TreeSet events = collectList("]");
            
            if (runtime) {
            	TreeNode[] nodes = new TreeNode[events.size()];
            	int i = 0;
            	for (Iterator it = events.iterator(); it.hasNext(); ++i) {
                  nodes[i] = new EventNode(((Integer)it.next()).intValue(), repository);            	    
            	}
            	
            	result = new AndParallelNode(nodes);
            	
            }
            else {
            	result = new AtomicNode(repository.addAtomicItem(events), events, repository);
            }
            
        } 
        
        else if (next_token.equals("")) {
            result = null;
        } else { // it has to be an action token or abbreviation
            int protstart = source.getIndex() - next_token.length() - 1;

            if (!isTokenID)
                throw new SyntaxErrorException("identifier expected, last token " + next_token);
            String event = next_token;
            next_token = nextToken();
            if (next_token == "{") {
                // nested call abbreviation
                next_token = nextToken();
                TreeNode pom = alternative();

                if (next_token != "}")
                    throw new SyntaxErrorException("} expected");

                int protstop = source.getIndex() - next_token.length();

                next_token = nextToken();

                //if (!event.startsWith("?"))
                //    event = "?" + event;
                EventNode start = new EventNode(repository.addItem(event + "^"), repository);
                start.protocol = event + "^";

                EventNode stop;

                /*
                 * stop = new ActionToken(
                 * stop.name.substring(0,stop.name.length()-1) + "$" );
                 */
                String rev = getReverseAction(event);
                stop = new EventNode(repository.addItem(rev + "$"), repository);
                stop.protocol = rev + "$";
                //stop = new EventNode(repository.addItem(event + "$"));

                /*
                 * pom = new SequenceEvent( new ProtocolToken( start ), pom );
                 * pom = new ProtocolSequence( pom, new ProtocolToken( stop ) );
                 */
                TreeNode[] nodearray = new TreeNode[3];
                nodearray[0] = start;
                nodearray[1] = pom;
                nodearray[2] = stop;
                result = new SequenceNode(nodearray);
                result.protocol = protocol.substring(protstart, protstop);

                Debug.println(3, "Nested call abbreviation:");
                //((Printer)pom).Print(3);

            } else { // action token or simple call abbrevation
                EventNode a = new EventNode(repository.addItem(event), repository);
                if (isSimpleCall(event)) { // simple call abbreviation
                    EventNode start = new EventNode(repository.addItem(event + "^"), repository);
                    start.protocol = event + "^";
                    EventNode stop = new EventNode(repository.addItem(getReverseAction(event + "$")), repository);
                    stop.protocol = getReverseAction(event + "$");

                    TreeNode[] nodearray = new TreeNode[2];
                    nodearray[0] = start;
                    nodearray[1] = stop;
                    result = new SequenceNode(nodearray);
                    result.protocol = event;
                } else { // action token
                    result = a;
                    result.protocol = event;
                }
            }
        }
        return result;
    }

    /**
     * The main entry point for building the automaton for a behavior protocol.
     * Using all private methods of this class, this method takes a string as
     * the argument and then it builds the automton by invoking Build for reader
     * argument
     * 
     * @param reader
     *            a reader containing a protocol to be build
     * @return built automaton
     * @throws SyntaxErrorException
     *             in case of badly formed protocols
     */
    public TreeNode build(ProtocolReader reader, boolean runtime) throws SyntaxErrorException {
        Debug.println(1, "Protocol Build: start");
        try {
        		this.runtime = runtime;
            source = reader;
            protocol = new String();
            next_char = (char) source.read();
            if (next_char == -1)
                return null;
            
            next_token = nextToken();
            TreeNode m = adjustment();
            if (next_token != "")
                throw new SyntaxErrorException("Unexpected token: '" + next_token + "'.");
            
            Debug.println(1, "Protocol Build: finished");
            //((Printer)m).Print( 2 );
            return m;
        } catch (IOException e) {
            return null;
        }
    }

}

